/**
 * #### 五子棋棋子颜色类型；0：无棋子，1：黑棋，2：白棋
 */
export type GoColor = 0 | 1 | 2;

/**
 * #### 五子棋棋盘坐标类型
 */
export type Coord = [number, number];

/**
 * #### 五子棋坐标移动函数类型
 */
type Movement = (p: Coord) => Coord;

/**
 * #### 判断棋盘上某个点是否为相同颜色有效
 * @param board 棋盘
 * @param point 位置坐标
 * @param color 棋子颜色
 * @returns boolean
 */
function isValid(board: GoColor[][], point: Coord, color: GoColor) {
  // 获取棋盘的行数和列数
  const row = board.length,
    col = board[0].length;
  // 获取点的坐标
  const [x, y] = point;
  // 判断点是否在棋盘范围内
  if (x < 0 || x >= row || y < 0 || y >= col) {
    return false;
  }
  // 判断点的颜色是否为指定颜色
  return board[x][y] === color;
}

/**
 * #### 创建一个函数，利用双指针判断棋盘上四个方向上是否大于于等于5个相同颜色的棋子
 * @param p1Movement 第一个方向上的移动函数
 * @param p2Movement 第二个方向上的移动函数
 * @returns
 */
function createIsWin(p1Movement: Movement, p2Movement: Movement) {
  // 返回一个函数，该函数接受棋盘、坐标和颜色作为参数
  return function (board: GoColor[][], point: Coord, color: GoColor) {
    // 初始化计数器
    let count = 0;
    // 获取两个方向上的下一个点
    let p1 = p1Movement(point),
      p2 = p2Movement(point);
    // 无限循环
    while (true) {
      // 初始化两个方向的标志位
      let p1Valid = false,
        p2Valid = false;
      // 判断第一个方向上的点是否有效
      if (isValid(board, p1, color)) {
        // 如果有效，计数器加1，并将标志位设为true
        count++;
        p1Valid = true;
        // 获取下一个点
        p1 = p1Movement(p1);
      }
      // 判断第二个方向上的点是否有效
      if (isValid(board, p2, color)) {
        // 如果有效，计数器加1，并将标志位设为true
        count++;
        p2Valid = true;
        // 获取下一个点
        p2 = p2Movement(p2);
      }
      // 如果计数器大于等于5，返回true
      if (count >= 5) {
        return true;
      }
      // 如果两个方向上的点都无效，返回false
      if (!p1Valid && !p2Valid) {
        return false;
      }
    }
  };
}

// 创建一个函数，用于判断是否在水平方向上获胜
const isWinHorizontal = createIsWin(
  ([x, y]) => [x + 1, y], // 向右移动一格
  ([x, y]) => [x - 1, y] // 向左移动一格
);
// 创建一个函数，用于判断是否在垂直方向上获胜
const isWinVertical = createIsWin(
  ([x, y]) => [x, y + 1], // 向下移动一格
  ([x, y]) => [x, y - 1] // 向上移动一格
);
// 创建一个函数，用于判断是否在对角线方向上获胜
const isWinDiagonal = createIsWin(
  ([x, y]) => [x + 1, y + 1], // 向右下移动一格
  ([x, y]) => [x - 1, y - 1] // 向左上移动一格
);
// 创建一个函数，用于判断是否在反对角线方向上获胜
const isWinAntiDiagonal = createIsWin(
  ([x, y]) => [x + 1, y - 1], // 向右上移动一格
  ([x, y]) => [x - 1, y + 1] // 向左下移动一格
);

/**
 * #### 五子棋判断是否获胜
 * @author duyi
 * @date 2025-04-12
 * @see https://www.bilibili.com/video/BV19vyHYeEgK?spm_id_from=333.788.videopod.sections&vd_source=9efadbd9bd76b9c14f9589f260a0c1ac
 * @param board 棋盘
 * @param point 棋盘坐标
 * @param color 棋子颜色；0：无棋子，1：黑棋，2：白棋
 * @returns boolean
 */
export function isWin(board: GoColor[][], point: Coord, color: GoColor) {
  return (
    // 判断在水平方向上是否获胜
    isWinHorizontal(board, point, color) ||
    // 判断在垂直方向上是否获胜
    isWinVertical(board, point, color) ||
    // 判断在斜方向上是否获胜
    isWinDiagonal(board, point, color) ||
    // 判断在反斜方向上是否获胜
    isWinAntiDiagonal(board, point, color)
  );
}

export function init2DArray(row: number, col: number, intiVal: GoColor = 0) {
  return Array.from({ length: row }, () =>
    Array.from({ length: col }, () => intiVal)
  );
}
